{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "1bd7cdd6-f48d-4100-ba04-18da2eca556b",
   "metadata": {},
   "source": [
    "# Quantum Phase Estimation\n",
    "\n",
    "The quantum phase estimation (QPE) function estimates the phase of an eigenvector of a unitary function.\n",
    "More precisely, given a unitary function $F$ and an input containing a quantum variable with a state $|\\psi\\rangle$ such that $F(|\\psi\\rangle)=e^{2\\pi i\\nu}|\\psi\\rangle$,\n",
    "the phase estimation function outputs an estimation of $\\nu$ as a fixed-point binary number.\n",
    "\n",
    "Phase estimation is frequently used as a subroutine in other quantum algorithms such as Shor's algorithm and quantum algorithms for solving linear systems of equations (HHL algorithm).\n",
    "Theoretical details are in Ref. [[1]](#1)."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "eeb1258a-e060-46ec-adf3-6db85caaa41c",
   "metadata": {},
   "source": [
    "Function: `qpe`\n",
    "\n",
    "Arguments:\n",
    "\n",
    "- `unitary: QCallable` - The unitary operation for which the qpe estimation the eigenvalues\n",
    "- `phase: QNum` - The output of the qpe, holding the phase as a number in the range $[0, 1)$\n",
    "\n",
    "Function: `qpe_flexible`\n",
    "\n",
    "The function is suitalbe when wants to specialize the way the power of a unitary is defined, other than using the naive power. For example it can used with exponentiaing hamiltonians or for shor's algorithm.\n",
    "\n",
    "Arguments:\n",
    "\n",
    "- `unitary_with_power: QCallable[CInt]` - Power of a unitary. Accepts as argument the power of the unitary to apply.\n",
    "- `phase: QNum`"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8814160e-d3ea-437c-be04-101572c0ebb1",
   "metadata": {},
   "source": [
    "## Examples\n",
    "\n",
    "### Example 1: QPE of a function\n",
    "\n",
    "This example shows how to perform a simple phase estimation:\n",
    "\n",
    "1. Initialize the state $|3\\rangle$ over two qubits.\n",
    "2. Apply a phase estimation on the the controlled-RZ gate, represeneted by the unitary matrix:\n",
    "\n",
    "$$\n",
    "\\begin{pmatrix}\n",
    "1 & 0 & 0 & 0 \\\\\n",
    "0 & e^{-i\\frac{\\lambda}{2}} & 0 & 0 \\\\\n",
    "0 & 0 & 1 & 0 \\\\\n",
    "0 & 0 & 0 & e^{i\\frac{\\lambda}{2}}\n",
    "\\end{pmatrix}\n",
    "$$\n",
    "\n",
    "The expected phase variable should encode $\\frac{\\lambda}{4\\pi}$, the phase of the eigenvalue of the $|3\\rangle$ state.\n",
    "Choosing $\\lambda = \\pi$, the expected result is $\\frac{1}{4}$, represented in binary by `01`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "bed3f9b5-5b1b-4602-aea5-711dd1a9c43b",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T13:26:32.821172Z",
     "iopub.status.busy": "2024-05-07T13:26:32.819608Z",
     "iopub.status.idle": "2024-05-07T13:26:35.604943Z",
     "shell.execute_reply": "2024-05-07T13:26:35.604186Z"
    }
   },
   "outputs": [],
   "source": [
    "from classiq import (\n",
    "    CRZ,\n",
    "    Output,\n",
    "    QArray,\n",
    "    QBit,\n",
    "    QNum,\n",
    "    allocate,\n",
    "    allocate_num,\n",
    "    create_model,\n",
    "    inplace_prepare_int,\n",
    "    qfunc,\n",
    "    qpe,\n",
    ")\n",
    "from classiq.qmod.symbolic import pi\n",
    "\n",
    "QPE_RESOLUTION = 2\n",
    "\n",
    "\n",
    "@qfunc\n",
    "def main(state: Output[QArray[QBit]], phase: Output[QNum]):\n",
    "    allocate(2, state)\n",
    "    allocate_num(QPE_RESOLUTION, False, QPE_RESOLUTION, phase)\n",
    "\n",
    "    inplace_prepare_int(3, state)\n",
    "    qpe(unitary=lambda: CRZ(pi, state[0], state[1]), phase=phase)\n",
    "\n",
    "\n",
    "qmod = create_model(main)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "2974e928-0a4f-495d-9bc5-f7d908b7e84f",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T13:26:35.609762Z",
     "iopub.status.busy": "2024-05-07T13:26:35.608303Z",
     "iopub.status.idle": "2024-05-07T13:26:38.105541Z",
     "shell.execute_reply": "2024-05-07T13:26:38.104881Z"
    }
   },
   "outputs": [],
   "source": [
    "from classiq import synthesize, write_qmod\n",
    "\n",
    "write_qmod(qmod, \"qpe\")\n",
    "qprog = synthesize(qmod)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "94c28c4f-9065-45c9-8020-a682c8fae00c",
   "metadata": {},
   "source": [
    "Show the actual results:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "359b2840-0149-4c74-9748-b4fbb2728367",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T13:26:38.108510Z",
     "iopub.status.busy": "2024-05-07T13:26:38.108021Z",
     "iopub.status.idle": "2024-05-07T13:26:39.436520Z",
     "shell.execute_reply": "2024-05-07T13:26:39.435826Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Results: [{'state': 3.0, 'phase': 0.25}: 1000]\n"
     ]
    }
   ],
   "source": [
    "from classiq import execute\n",
    "\n",
    "res = execute(qprog).result()[0].value\n",
    "print(\"Results:\", res.parsed_counts)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "050e47b8-65e0-4acc-8ed2-f1365dc734a7",
   "metadata": {},
   "source": [
    "### Example 2: Flexible QPE\n",
    "\n",
    "The following examples will specifiy directly how to take powers in the QPE. The unitary function is `suzuki_trotter`, where the number of repetitions will be 1. In the case of diagonal hamiltonian it be exact exponentiation of the hamiltoian.\n",
    "\n",
    "Take the following matrix:\n",
    "$$\n",
    "\\begin{pmatrix}\n",
    "0 & 0 & 0 & 0 \\\\\n",
    "0 & \\tfrac{1}{4} & 0 & 0 \\\\\n",
    "0 & 0 & \\tfrac{1}{2} & 0 \\\\\n",
    "0 & 0 & 0 & \\tfrac{3}{4} \\\\\n",
    "\\end{pmatrix}\n",
    "$$\n",
    "\n",
    "Represented by the hamiltonian:\n",
    "\n",
    "$H = -\\frac{1}{8}Z_0I_1 - \\frac{1}{4}I_0Z_1 + \\frac{3}{8}I_0I_1$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "2202730d-c9ca-475e-8e15-b66c96c11f9c",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T13:26:39.440552Z",
     "iopub.status.busy": "2024-05-07T13:26:39.439437Z",
     "iopub.status.idle": "2024-05-07T13:26:39.465683Z",
     "shell.execute_reply": "2024-05-07T13:26:39.464886Z"
    }
   },
   "outputs": [],
   "source": [
    "from classiq import (\n",
    "    Output,\n",
    "    Pauli,\n",
    "    PauliTerm,\n",
    "    QArray,\n",
    "    QBit,\n",
    "    QNum,\n",
    "    allocate,\n",
    "    allocate_num,\n",
    "    create_model,\n",
    "    hadamard_transform,\n",
    "    qfunc,\n",
    "    qpe_flexible,\n",
    "    suzuki_trotter,\n",
    ")\n",
    "from classiq.qmod.symbolic import pi\n",
    "\n",
    "QPE_RESOLUTION = 2\n",
    "\n",
    "HAMILTONIAN = [\n",
    "    PauliTerm(pauli=[Pauli.I, Pauli.Z], coefficient=-0.125),\n",
    "    PauliTerm(pauli=[Pauli.Z, Pauli.I], coefficient=-0.25),\n",
    "    PauliTerm(pauli=[Pauli.I, Pauli.I], coefficient=0.375),\n",
    "]\n",
    "\n",
    "\n",
    "@qfunc\n",
    "def main(state: Output[QArray[QBit]], phase: Output[QNum]):\n",
    "    allocate(2, state)\n",
    "    allocate_num(QPE_RESOLUTION, False, QPE_RESOLUTION, phase)\n",
    "\n",
    "    hadamard_transform(state)\n",
    "    qpe_flexible(\n",
    "        lambda power: suzuki_trotter(\n",
    "            HAMILTONIAN,\n",
    "            evolution_coefficient=-2 * pi * (power),\n",
    "            order=1,\n",
    "            repetitions=1,\n",
    "            qbv=state,\n",
    "        ),\n",
    "        phase,\n",
    "    )\n",
    "\n",
    "\n",
    "qmod = create_model(main)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "811cf6c4-9bb1-433b-9291-69148013b2d3",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T13:26:39.470737Z",
     "iopub.status.busy": "2024-05-07T13:26:39.469563Z",
     "iopub.status.idle": "2024-05-07T13:26:41.647045Z",
     "shell.execute_reply": "2024-05-07T13:26:41.646433Z"
    }
   },
   "outputs": [],
   "source": [
    "from classiq import synthesize, write_qmod\n",
    "\n",
    "write_qmod(qmod, \"qpe_flexible\")\n",
    "qprog = synthesize(qmod)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0b0a0eee-d1ac-489e-927c-7731bc9ef4a2",
   "metadata": {},
   "source": [
    "Show the actual results:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "e97a50fd-6080-47f5-b522-f4ce6bfeb503",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T13:26:41.650635Z",
     "iopub.status.busy": "2024-05-07T13:26:41.650079Z",
     "iopub.status.idle": "2024-05-07T13:26:42.996491Z",
     "shell.execute_reply": "2024-05-07T13:26:42.995748Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Results: [{'state': 3.0, 'phase': 0.75}: 265, {'state': 0.0, 'phase': 0.0}: 252, {'state': 1.0, 'phase': 0.25}: 245, {'state': 2.0, 'phase': 0.5}: 238]\n"
     ]
    }
   ],
   "source": [
    "from classiq import execute\n",
    "\n",
    "res = execute(qprog).result()[0].value\n",
    "print(\"Results:\", res.parsed_counts)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "611fa4cb-eb30-41d0-8fbd-a6f1475ebc64",
   "metadata": {},
   "source": [
    "## References\n",
    "\n",
    "<a name=\"1\">[1]</a> A. Yu. Kitaev Barenco et al, Quantum Measurements and the Abelian Stabilizer Problem,\n",
    "(1995). https://doi.org/10.48550/arXiv.quant-ph/9511026"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
